package com.wwg.widget.impl;

import android.graphics.Rect;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.wwg.widget.ISpringDraggable;
import com.wwg.widget.fake.op.IFakeMaster;

public class SpringBoardDragHelper {
    private static final String TAG = "SBDragHelper";
    private static final int[] INVALID_DETECT_RESULT = new int[] {-1, -1};
    private final ISpringDraggable mDraggable;

    private int mChildWith;
    private int mChildHeight;

    private int mDrapedXE;
    private int mDrapedYE;
    private int mCrowdXE;
    private int mCrowdYE;

    private int mCurrentIndex = -1;
    @Nullable
    private SpringBoardContentView.ViewHolder mPendingCurrentTarget = null;

    private int mDrapeRobe = -1;
    private int mCrowdOut = -1;

    private int mConfirmedDrapeRobe = -1;

    @Nullable
    private IFakeMaster mFakeMaster;

    public SpringBoardDragHelper(SpringBoardContentView draggable) {
        mDraggable = draggable;
    }

    void updateChildSize(int childWidth, int childHeight) {
        mChildWith = childWidth;
        mDrapedXE = childWidth / 10;
        mChildHeight = childHeight;
        mDrapedYE = childHeight / 10;

        mCrowdXE = childWidth / 6;
        mCrowdYE = childHeight / 6;
    }

    public void injectMaster(IFakeMaster master) {
        mFakeMaster = master;
    }


    void startCheckDrag(SpringBoardContentView.ViewHolder target) {
        mPendingCurrentTarget = target;
    }

    @Nullable
    SpringBoardContentView.ViewHolder getPendingCurrentTarget() {
        return mPendingCurrentTarget;
    }

    void startPendingAnimate(long duration) {
        if (mPendingCurrentTarget != null) {
            View itemView = mPendingCurrentTarget.itemView;
            itemView.animate().cancel();
            itemView.animate()
                    .scaleX(1.2f).scaleY(1.2f)
                    .setDuration(duration)
                    .start();
        }
    }

    void interruptCheckDrag() {
        if (mPendingCurrentTarget != null) {
            View itemView = mPendingCurrentTarget.itemView;
            itemView.animate().cancel();
            itemView.setScaleX(1f);
            itemView.setScaleY(1f);
        }

        mPendingCurrentTarget = null;
    }

    boolean ensureDrag(SpringBoardContentView.ViewHolder target) {
        if (mPendingCurrentTarget != null) {
            if (target.index == mPendingCurrentTarget.index) {
                return true;
            }

            interruptCheckDrag();
            return false;
        }

        return false;
    }

    void fixedDragTarget(SpringBoardContentView.Adapter<SpringBoardContentView.ViewHolder> adapter) {
        if (mPendingCurrentTarget == null) {
            return;
        }

        mCurrentIndex = mPendingCurrentTarget.index;

        if (mFakeMaster != null) {
            mFakeMaster.feedbackFake(mPendingCurrentTarget, adapter.getItem(mCurrentIndex));
        }

        mPendingCurrentTarget = null;
    }

    public int getDrapeRobe() {
        return mDrapeRobe;
    }

    public void confirmedDrapeRobe() {
        mConfirmedDrapeRobe = mDrapeRobe;
    }

    private void clearConfirmedDrapeRobe() {
        mConfirmedDrapeRobe = -1;
    }

    public int getConfirmedDrapeRobe() {
        return mConfirmedDrapeRobe;
    }

    public int getCrowdOut() {
        return mCrowdOut;
    }

    boolean hasPendingDragTarget() {
        return mPendingCurrentTarget != null;
    }

    void releasePendingDragTarget() {
        interruptCheckDrag();
    }

    void resetCurrent() {
        mCurrentIndex = -1;
    }

    void updateCurrent(int index) {
        mCurrentIndex = index;
    }

    int getCurrentIndex() {
        return mCurrentIndex;
    }

    void feedbackDetectResult(int[] result) {
        mDrapeRobe = result[0];
        mCrowdOut = result[1];

        if (mConfirmedDrapeRobe != mDrapeRobe) {
            clearConfirmedDrapeRobe();
        }
    }

    void clearDetectResult() {
        mDrapeRobe = -1;
        mCrowdOut = -1;

        clearConfirmedDrapeRobe();
    }

    /**
     * 滑动时，区域检测。返回值第一个是重叠者位置（文件夹），第二个是被挤走者位置。
     * 两者不会同时存在，但可能同时不存在。
     * -1为无效值，代表不存在。
     * @param x x based on parent
     * @param y y based on parent
     * @return detection result, first is the draped robe, second is crowd out
     */
    int[] detectRegion(int x, int y) {
        Rect currentRect = getCurrentCorrectWithSpringBoard();
        if (currentRect == null) {
            return INVALID_DETECT_RESULT;
        }

        int count = mDraggable.getDraggableCount();
        int span = mDraggable.getHorizontalSpan();
        int horizontalGap = mDraggable.getHorizontalGap();
        int verticalGap = mDraggable.getVerticalGap();

        int crowdColumn = -1;
        int crowdRow = -1;
        int drapedColumn = -1;
        int drapedRow = -1;
        int column = mCurrentIndex % span;
        int unitX = mChildWith + horizontalGap;
        int deltaX = x - currentRect.left;
        if (deltaX > 0) {
            for (int i = column; i < span; i++) {
                int xOffset = (i - column) * unitX;
                int trial = xOffset - deltaX;
//                debugDetect("deltaX > 0==> i = " + i + " trial = " + trial + " fixed = " + mDrapedXE + " " + mChildWith);
                if ((trial >= -mDrapedXE) && (trial <= mDrapedXE)) {
                    drapedColumn = i;
                    break;
                } else if ((trial <= -mCrowdXE) && (trial >= (mCrowdXE - mChildWith))) {
                    crowdColumn = i;
                    break;
                }
            }
        } else if (deltaX < 0) {
            for (int i = column; i >= 0; i--) {
                int xOffset = (i - column) * unitX;
                int trial = xOffset - deltaX;
//                debugDetect("deltaX < 0==> i = " + i + " trial = " + trial + " fixed = " + mDrapedXE + " " + mChildWith);
                if ((trial >= -mDrapedXE) && (trial <= mDrapedXE)) {
                    drapedColumn = i;
                    break;
                } else if ((trial >= mCrowdXE) && (trial <= (mChildWith - mCrowdXE))) {
                    crowdColumn = i;
                    break;
                }
            }
        }

        debugDetect("drapedColumn " + drapedColumn + " crowdColumn " + crowdColumn);
        if (drapedColumn == -1 && crowdColumn == -1) {
            return INVALID_DETECT_RESULT;
        }

        int totalRow = count / span;
        int row = mCurrentIndex / span;
        int unitY = mChildHeight + verticalGap;
        int deltaY = y - currentRect.top;
        if (deltaY > 0) {
            for (int i = row; i <= totalRow; i++) {
                int yOffset = (i - row) * unitY;
                int trial = yOffset - deltaY;
                debugDetect("deltaY > 0==> i = " + i + " trial = " + trial + " fixed = " + mDrapedYE + " " + mCrowdYE);
                if ((trial >= -mDrapedYE) && (trial <= mDrapedYE)) {
                    drapedRow = i;
                    break;
                } else if ((trial >= -mCrowdYE) && (trial <= mCrowdYE)) {
                    crowdRow = i;
                    break;
                }
            }
        } else if (deltaY < 0) {
            for (int i = row; i >= 0; i--) {
                int yOffset = (i - row) * unitY;
                int trial = yOffset - deltaY;
                debugDetect("deltaY < 0==> i = " + i + " trial = " + trial + " fixed = " + mDrapedYE + " " + mCrowdYE);
                if ((trial >= -mDrapedYE) && (trial <= mDrapedYE)) {
                    drapedRow = i;
                    break;
                } else if ((trial >= -mCrowdYE) && (trial <= mCrowdYE)) {
                    crowdRow = i;
                    break;
                }
            }
        }

        debugDetect("drapedRow = " + drapedRow + " crowdRow = " + crowdRow);
        if (drapedRow == -1 && crowdRow == -1) {
            return INVALID_DETECT_RESULT;
        }

        // drape robe (文件夹)
        int drapeRobe = -1;
        if ((drapedRow != -1) && (drapedColumn != -1)) {
//            Log.i(TAG, "row " + drapedRow + " column " + drapedColumn + " index " + drapeRobe);
            drapeRobe = drapedRow * span + drapedColumn;
            if (drapeRobe >= count || drapeRobe == mCurrentIndex) {
                drapeRobe = -1;
            }
        }

        //crowd out (被挤走)
        int crowdOut = -1;
        if ((crowdRow != -1) && (crowdColumn != -1)) {
//            Log.i(TAG, "row " + crowdRow + " column " + crowdColumn + " index " + crowdOut);
            crowdOut = crowdRow * span + crowdColumn;
            if (crowdOut >= count || crowdOut == mCurrentIndex) {
                crowdOut = -1;
            }
        } else if (drapeRobe == -1) {
            if ((crowdRow != -1) && (drapedColumn != -1)) {
                crowdOut = crowdRow * span + drapedColumn;
                if (crowdOut >= count || crowdOut == mCurrentIndex) {
                    crowdOut = -1;
                }
            } else if (crowdColumn != -1 && drapedRow != -1) {
                crowdOut = drapedRow * span + crowdColumn;
                if (crowdOut >= count || crowdOut == mCurrentIndex) {
                    crowdOut = -1;
                }
            }
        }

        return new int[] {drapeRobe, crowdOut};
    }

    @Nullable
    private Rect getCurrentCorrectWithSpringBoard() {
        return getRectCorrectWithSpringBoard(mCurrentIndex);
    }

    @Nullable
    public Rect getRectCorrectWithSpringBoard(int index) {
        Rect rect = getRectCorrectWithParent(index);
        if (rect == null) {
            return null;
        }

        int xOffset = mDraggable.getXOffsetRelativeToSpringBoard();
        int yOffset = mDraggable.getYOffsetRelativeToSpringBoard();
        rect.left += xOffset;
        rect.right += xOffset;
        rect.top += yOffset;
        rect.bottom += yOffset;

        return rect;
    }

    public Rect getLogicRectCorrectWithSpringBoard(int index) {
        Rect rect = calculateRect(index);

        int xOffset = mDraggable.getXOffsetRelativeToSpringBoard();
        int yOffset = mDraggable.getYOffsetRelativeToSpringBoard();
        rect.left += xOffset;
        rect.right += xOffset;
        rect.top += yOffset;
        rect.bottom += yOffset;

        return rect;
    }

    @Nullable
    Rect getRectCorrectWithParent(int index) {
        int count = mDraggable.getDraggableCount();
        if (index < 0 || index >= count) {
            return null;
        }

        return calculateRect(index);
    }

    private Rect calculateRect(int index) {
        int span = mDraggable.getHorizontalSpan();
        int horizontalGap = mDraggable.getHorizontalGap();
        int verticalGap = mDraggable.getVerticalGap();

        int column = index % span;
        int left = (mChildWith + horizontalGap) * column;
        int right = left + mChildWith;

        int row = index / span;
        int top = (mChildHeight + verticalGap) * row;
        int bottom = top + mChildHeight;

        int paddingStart = mDraggable.getPaddingStart();
        int paddingTop = mDraggable.getPaddingTop();
        left += paddingStart;
        right += paddingStart;
        top += paddingTop;
        bottom += paddingTop;

        return new Rect(left, top, right, bottom);
    }

    private void debugDetect(String message) {
        // Log.w("debug_detect", message);
    }
}
